package no.ffi.gismoidm.pubsub;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import no.ffi.gismoidm.utils.IdentityStatement;
import no.fongen.os.FIFO;

/**
 * Class that represents a message client
 * @author anders
 */
public class MessageClient extends MessageNeighbor {
    private List<Subscription> subscriptions;
    private ClientTable clientTable;
    private ForwardingTable forwardingTable;
    
    // Public constructor
    public MessageClient(String clientId,IdentityStatement peerIS,FIFO outward, String myId) {
        messageNeighborID = clientId;
        this.peerIS = peerIS;
        outwardMessages = outward;
        subscriptions = new ArrayList();
        
        // Add client to Client table
        clientTable = ClientTable.getInstance();
        System.out.println("Adding client to client table");
        clientTable.addClient(this);
        // Get reference to node's forwarding table object
        forwardingTable = ForwardingTable.getInstance();
    }
    
    public void receiveMessage(Object oo) {
        if (oo instanceof Publication) {
            Publication p = (Publication)oo;
            // Identity statement is added by the router, NOT the publisher
            p.setIdentityStatement(peerIS);
            forwardingTable.publicationReceived(p, this);
        } else if (oo instanceof Subscription) {
            Subscription s = (Subscription)oo;
            if (s.getDelete()) 
                  removeSubscription();
            else
                setSubscription(Collections.singletonList(s)); 
        } else if (oo instanceof List) {
            setSubscription((List<Subscription>)oo); 
        } else if (oo instanceof IdentityStatement) {
            // New IS received (and validated) from peer
            peerIS = (IdentityStatement)oo;
        }
    }
    
    // Log off the client (connection terminated)
    public void terminateClient() {
        System.out.println("Removing table entry for " + messageNeighborID);
        clientTable.removeClient(this);
        outwardMessages.nonblockput(new TerminateSignal());
        System.out.println("Client table has now " + clientTable.size() + " entries");
    }
    
//    public synchronized long addSubscription(Subscription sub) {
//        subscriptions.add(sub); // TODO Make sure that the client cannot tweak his properties
//        // calculate a new aggregate subscription for this node
//        forwardingTable.newLocalAggSub(
//                clientTable.generateLocalAggSub());
//        return sub.getReference();
//    }
    
    public void setSubscription(List<Subscription> sub) {
        subscriptions = sub;
        forwardingTable.newLocalAggSub(
                clientTable.generateLocalAggSub());
    }

    public synchronized void removeSubscription() {
        subscriptions = new ArrayList();
        forwardingTable.newLocalAggSub(
                clientTable.generateLocalAggSub());        
    }

//    public synchronized void removeSubscription(long reference) {
//        Iterator<Subscription> it = subscriptions.iterator();
//        while (it.hasNext()) {
//            Subscription s1 = it.next();
//            long ref = s1.getReference();
//            if (ref==reference) { 
//                it.remove();
//                return;
//            }
//        }
//        forwardingTable.newLocalAggSub(
//                clientTable.generateLocalAggSub());
//    }
    
    public synchronized boolean matchSubscriptions(Publication pub) {
        Iterator<Subscription> it = subscriptions.iterator();
        while (it.hasNext()) {
            Subscription s1 = it.next();
            if (s1.publicationMatch(pub, peerIS
                    .getAttributeAsProperties()))
                    return true;
        }
        return false;
    }
    
    public List<Subscription> getSubscriptions() { return subscriptions; }
    
}
